package net.chrisrichardson.ec2deployer.cluster

import net.chrisrichardson.ec2deployer.applications.TomcatServer
import net.chrisrichardson.ec2deployer.core.EC2Server
import org.apache.commons.logging.*
import net.chrisrichardson.ec2deployer.applications.WebApp
import net.chrisrichardson.ec2deployer.core.CloudManager
import net.chrisrichardson.ec2deployer.core.ProcessExecutionException;
import net.chrisrichardson.ec2deployer.core.EC2
import java.lang.reflect.Constructor;

class AppServerTier extends AbstractTier {

    Log logger = LogFactory.getLog(getClass())

    ClusterSpec spec

    List<TomcatServer> tomcats = []
    DatabaseTier databaseTier
    TerracottaTier terracottaTier
    WebServerTier webServerTier

    protected AppServerTier() {}

    AppServerTier(CloudManager ec2, ClusterSpec spec, databaseTier, TerracottaTier terracottaTier) {
        super(ec2)
        this.spec = spec
        this.databaseTier = databaseTier
        this.terracottaTier = terracottaTier
    }

    def saveState(result) {
        if (!tomcats.empty)
            result.put("tomcats", tomcats.host.instanceId)
    }

    def restoreState(serverState) {
        this.tomcats = restoreTomcats(serverState["tomcats"])
    }

    def restoreTomcats(previousInstanceIds) {
        previousInstanceIds.collect { makeTomcat(ec2.findInstance(it)) 
        }
    }

    def assignHosts(hosts) {
        tomcats = []

        def tomcatsIt = hosts.tomcatsIterator

        spec.numberOfTomcats.times {tomcats.add(makeTomcat(tomcatsIt.next()))}

    }

    def makeTomcat(EC2Server server) {
        def awsProperties = ec2.awsProperties
        if (awsProperties.exists("appServerClass")) {
            Class type = Class.forName(awsProperties.appServerClass)
            Constructor cons = type.getConstructor(ClusterSpec, EC2Server, DatabaseTier, TerracottaTier, WebServerTier)
            return cons.newInstance(spec, server, databaseTier, terracottaTier, webServerTier)
        } else {
            return new TomcatServer(spec, server, databaseTier, terracottaTier, webServerTier)
        }
    }

    void performStart() {
        phase ("Configuring Tomcat") {
            tomcats*.startTomcat()
        }

        phase ("Waiting for web apps to deploy") {
            tomcats*.waitUntilRunning()
        }

    }

    def restart() {
        // TODO - we only need to do part of what startTomcat() does
        tomcats*.startTomcat()
        tomcats*.waitUntilRunning()
    }

    def uploadWebAppsToS3() {
        spec.webApps*.uploadToS3(ec2.awsProperties, spec.bucketPrefix)
    }

    def redeploy() {
        uploadWebAppsToS3()
        restart()
    }

    List<EC2Server> getServers() {
        tomcats.host
    }

    def getNumberOfTomcats() {
        tomcats.size()
    }

    def getSarMonitors() {
        tomcats.sarMonitor
    }

    void updateWar(String explodedWar, String s3Path) {
        tomcats[0].updateWar(explodedWar, s3Path)
    }

    public void addAppServers(int n) {
        List<EC2Server> newInstances = ec2.newServers(n, spec.tomcatInstanceType)

        waitUntilRunning newInstances, new HashSet()

        List<TomcatServer> newTomcats = newInstances.collect { EC2Server instance -> makeTomcat(instance) }

        newTomcats*.startTomcat()
        newTomcats*.waitUntilRunning()

        tomcats.addAll(newTomcats)
   }

   	boolean isExpandable() {
   	  true // FIXME Impose a limit
   	}
   	
    boolean isShrinkable() {
      tomcats.size() > 1 // FIXME impose a limit
    }

    public List<EC2Server> addAppServer(CloudManager ec2) {
        List<EC2Server> newInstances = ec2.newServers(1, spec.tomcatInstanceType)
        List<TomcatServer> newTomcats = newInstances.collect { EC2Server instance -> makeTomcat(instance) }
        tomcats.addAll(newTomcats)
        newInstances
   }

   public void startTomcatOn(List<EC2Server> ec2Servers) {
   		List<String> instanceIds = ec2Servers.instanceId
        List<TomcatServer> newTomcats = tomcats.findAll { tomcat -> instanceIds.contains(tomcat.host.instanceId) } 
        newTomcats*.startTomcat()
        newTomcats*.waitUntilRunning()
   }

   public void createTomcatsRunningOn(List<EC2Server> ec2Servers) {
        List<TomcatServer> newTomcats = ec2Servers.collect { EC2Server instance -> makeTomcat(instance) }
        tomcats.addAll(newTomcats)
   }
   
   Collection<String> getContexts() {
   		spec.webApps.context
   }

    void removeTomcat(TomcatServer tomcat) {
        tomcats.remove(tomcat);
    }

    String snapshotWebApplications() {
        tomcats.get(0).snapshotWebApplications()
    }
}